﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Postnuclear.Dependency.Tree;
using Postnuclear.Test.Helpers;

namespace Postnuclear.Test.Dependency.Tree
  {
  /// <summary>
  /// Represents a test class for the <see cref="DependencyTreeElement{THolder, TMember}"/> class.
  /// </summary>
  [TestClass]
  public sealed class DependencyTreeElementTest
    : TestClassBase
    {
    #region Public

    // Methods

    /// <summary>
    /// Initializes this instance.
    /// </summary>
    [TestInitialize]
    public void Initialize()
      {
      Target = new Mock<IDependencyElement<object, object>>();
      Target.SetAndRaise(new object());

      Target1 = new Mock<IDependencyTreeElement<object>>();
      Target2 = new Mock<IDependencyTreeElement<object>>();
      Target3 = new Mock<IDependencyTreeElement<object>>();

      DependencyElement = new DependencyTreeElement<object, object>(Target.Object, Target1.Object, Target2.Object, Target3.Object);
      }

    /// <summary>
    /// A test for <see cref="DependencyTreeElement{THolder, TMember}"/> constructor to check exception if the dependency element is not specified.
    /// </summary>
    [TestMethod]
    [ExpectedException(typeof(ArgumentNullException))]
    public void CreateWithNullElementTest()
      {
      new DependencyTreeElement<object, object>(null, Target1.Object, Target2.Object, Target3.Object);
      }

    /// <summary>
    /// A test for <see cref="DependencyTreeElement{THolder, TMember}"/> constructor to check exception for null children collection.
    /// </summary>
    [TestMethod]
    [ExpectedException(typeof(ArgumentNullException))]
    public void CreateWithNullChildrenTest()
      {
      new DependencyTreeElement<object, object>(Target.Object, null);
      }

    /// <summary>
    /// A test for <see cref="DependencyTreeElement{THolder, TMember}"/> constructor to check exception for empty children collection.
    /// </summary>
    [TestMethod]
    [ExpectedException(typeof(ArgumentException))]
    public void CreateWithoutChildrenTest()
      {
      new DependencyTreeElement<object, object>(Target.Object);
      }

    /// <summary>
    /// A test for <see cref="INotifyValueChanged.ValueChanged" /> event.
    /// </summary>
    [TestMethod]
    public void ValueChangedTest()
      {
      int updatesCount = 0;
      EventHandler<EventArgs> updateHandler = (_, __) => ++updatesCount;
      DependencyElement.ValueChanged += updateHandler;

      DependencyElement.UpdateHolderValue(new object());
      Assert.AreEqual(0, updatesCount);

      Target.RaiseValueChanged();
      Assert.AreEqual(1, updatesCount);

      Target1.RaiseValueChanged();
      Assert.AreEqual(2, updatesCount);

      Target2.RaiseValueChanged();
      Assert.AreEqual(3, updatesCount);

      Target3.RaiseValueChanged();
      Assert.AreEqual(4, updatesCount);

      DependencyElement.ValueChanged -= updateHandler;
      Assert.AreEqual(4, updatesCount);

      Target.Object.UpdateHolderValue(new object());
      Assert.AreEqual(4, updatesCount);

      Target.RaiseValueChanged();
      Assert.AreEqual(4, updatesCount);

      Target1.RaiseValueChanged();
      Assert.AreEqual(4, updatesCount);

      Target2.RaiseValueChanged();
      Assert.AreEqual(4, updatesCount);

      Target3.RaiseValueChanged();
      Assert.AreEqual(4, updatesCount);
      }

    #endregion

    #region Private

    // Properties

    private Mock<IDependencyElement<object, object>> Target
      {
      get;
      set;
      }

    private Mock<IDependencyTreeElement<object>> Target1
      {
      get;
      set;
      }

    private Mock<IDependencyTreeElement<object>> Target2
      {
      get;
      set;
      }

    private Mock<IDependencyTreeElement<object>> Target3
      {
      get;
      set;
      }

    private DependencyTreeElement<object, object> DependencyElement
      {
      get;
      set;
      }

    #endregion
    }
  }
